package com.ysd.lis.service.qc.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.ysd.common.Result;
import com.ysd.lis.entity.qc.QcMaterial;
import com.ysd.lis.entity.qc.QcMaterialItem;
import com.ysd.lis.entity.qc.QcMaterialItemResult;
import com.ysd.lis.entity.qc.QcMaterialItemTarget;
import com.ysd.lis.mapper.qc.QcMaterialItemResultMapper;
import com.ysd.lis.mapper.qc.QcMaterialMapper;
import com.ysd.lis.request.qc.*;
import com.ysd.lis.service.SysBasDictDetailService;
import com.ysd.lis.service.qc.QcMaterialResultChartService;
import org.apache.commons.math.MathException;
import org.apache.commons.math.distribution.FDistributionImpl;
import org.apache.commons.math.distribution.TDistributionImpl;
import org.apache.commons.math3.distribution.NormalDistribution;
import org.apache.commons.math3.distribution.TDistribution;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.groupingBy;

/**
 * @program: server_lis
 * @author: liuQi
 * @create: 2024-01-09 17:46
 */
@Service
public class QcMaterialResultChartServiceImpl implements QcMaterialResultChartService {

    @Autowired
    QcMaterialItemResultMapper qcMaterialItemResultMapper;
    @Autowired
    QcMaterialMapper qcMaterialMapper;
    @Autowired
    QcMaterialItemServiceImpl qcMaterialItemService;
    @Autowired
    SysBasDictDetailService sysBasDictDetailService;

    /**
     * 获取统计图数据集
     * ==========================================Z图、LJ图======================================
     *
     * @return
     */
    @Override
    public Result getResultChartData(QcResultChartParam qcResultChartParam) {
        Map<String, Object> qcResultByQcItemId = getQcResultByQcItemId(qcResultChartParam);
        return Result.succ(1, "查询成功", qcResultByQcItemId);
    }

    /**
     * 获取Z图、LJ图下方统计第一个结果
     *
     * @param qcResultChartParam
     * @return
     */
    @Override
    public Result getResultChartTable1(QcResultChartParam qcResultChartParam) {

        return Result.succ(1, "查询成功！", null);
    }

    /**
     * 获取Z图、LJ图下方统计第二个结果
     *
     * @param
     * @return
     */
    @Override
    public Result getResultChartTable2(QcPageDto dto) {
        QcMaterialResultOutputDto resResult = new QcMaterialResultOutputDto();
        String itemDictId = dto.getItemDictId();
        String materialIds = dto.getMaterialIds();
        String[] split = materialIds.split(",");

        MPJLambdaWrapper<QcMaterialItemResult> queryWrapper = new MPJLambdaWrapper<>();
        queryWrapper
                .selectAs("ma", QcMaterial::getId, QcResultQueryVo::getMaterialId)
                .selectAs("ma", QcMaterial::getMaterialName, QcResultQueryVo::getMaterialName)
                .selectAs("ma", QcMaterial::getBatchNum, QcResultQueryVo::getBatchNum)
                .selectAs("ma", QcMaterial::getHorizontal, QcResultQueryVo::getHorizontal)
                .selectAs("ma", QcMaterial::getManufacturer, QcResultQueryVo::getManufacturer)
                .selectAs("ma", QcMaterial::getEnableDate, QcResultQueryVo::getEnableDate)
                .selectAs("ma", QcMaterial::getValidDate, QcResultQueryVo::getValidDate)
                .selectAs("ta", QcMaterialItemTarget::getId, QcResultQueryVo::getMaterialTargetId)
                .selectAs("ta", QcMaterialItemTarget::getEnableDate, QcResultQueryVo::getTaEnableDate)
                .selectAs("ta", QcMaterialItemTarget::getStopDate, QcResultQueryVo::getStopDate)
                .selectAs("ta", QcMaterialItemTarget::getMean, QcResultQueryVo::getMean)
                .selectAs("ta", QcMaterialItemTarget::getSd, QcResultQueryVo::getSd)
                .selectAs("ta", QcMaterialItemTarget::getCv, QcResultQueryVo::getCv)
                .selectAs(QcMaterialItemResult::getId, QcResultQueryVo::getId)
                .selectAs(QcMaterialItemResult::getMethod, QcResultQueryVo::getMethod)
                .selectAs(QcMaterialItemResult::getNumResult, QcResultQueryVo::getNumResult)
                .selectAs(QcMaterialItemResult::getResult, QcResultQueryVo::getResult)
                .selectAs(QcMaterialItemResult::getQcDate, QcResultQueryVo::getQcDate)
                .selectAs(QcMaterialItemResult::getQcSequence, QcResultQueryVo::getQcSequence)
                .selectAs(QcMaterialItemResult::getIsOutControl, QcResultQueryVo::getIsOutControl)
                .selectAs(QcMaterialItemResult::getOutControlRules, QcResultQueryVo::getOutControlRules)
                .selectAs(QcMaterialItemResult::getIsOutControlHandled, QcResultQueryVo::getIsOutControlHandled)
                .selectAs(QcMaterialItemResult::getIsAlarm, QcResultQueryVo::getIsAlarm)
                .selectAs(QcMaterialItemResult::getAlarmRules, QcResultQueryVo::getAlarmRules)
                .selectAs(QcMaterialItemResult::getIsBestInDay, QcResultQueryVo::getIsBestInDay)
                .selectAs(QcMaterialItemResult::getZ, QcResultQueryVo::getZ)
                .leftJoin(QcMaterial.class, "ma", QcMaterial::getId, QcMaterialItemResult::getMaterialId)
                .leftJoin(QcMaterialItem.class, "it", QcMaterialItem::getId, QcMaterialItemResult::getMaterialItemId)
                .leftJoin(QcMaterialItemTarget.class, "ta", QcMaterialItemTarget::getId, QcMaterialItemResult::getMaterialTargetId)
                .eq(QcMaterialItemResult::getItemDictId, itemDictId)
                .in(QcMaterialItemResult::getMaterialId, split)
                .eq(QcMaterialItemResult::getDelFlag, 0)
                .ge(QcMaterialItemResult::getQcDate, dto.getQCDateS())
                .le(QcMaterialItemResult::getQcDate, dto.getQCDateE())
                .orderByAsc(QcMaterialItemResult::getQcDate);
        List<QcResultQueryVo> results = qcMaterialItemResultMapper.selectJoinList(QcResultQueryVo.class, queryWrapper);
        List<String> xDateTimeList = results.stream().map(result -> DateUtil.format(result.getQcDate(), "MM.dd")).distinct().collect(Collectors.toList());
        resResult.setXDateTimeList(xDateTimeList);

        //List<String> iqcMaterialGroup = results.stream().map(QcResultQueryVo::getMaterialId).distinct().collect(Collectors.toList());
        Map<String, List<QcResultQueryVo>> iqcMaterialGroup = results.stream().collect(groupingBy(QcResultQueryVo::getMaterialId));
        Set<String> mIds = iqcMaterialGroup.keySet();

        for (String mid : mIds) {
            QcResultMaterialVo iQCMaterialItemEnty = new QcResultMaterialVo();
            List<QcResultQueryVo> queryVoList = iqcMaterialGroup.get(mid);
            iQCMaterialItemEnty.setName(queryVoList.get(0).getMaterialName());
            iQCMaterialItemEnty.setTotal(queryVoList.size());
            iQCMaterialItemEnty.setControlNum(queryVoList.stream().filter(v -> !v.getIsOutControl()).collect(Collectors.toList()).size());
            iQCMaterialItemEnty.setOutControlNum(queryVoList.stream().filter(v -> v.getIsOutControl()).collect(Collectors.toList()).size());
            iQCMaterialItemEnty.setControlRate(queryVoList.size() <= 0 ? 0 : (double) iQCMaterialItemEnty.getControlNum() / (double) queryVoList.size());
            iQCMaterialItemEnty.setOutControlRate(queryVoList.size() <= 0 ? 0 : (double) iQCMaterialItemEnty.getOutControlNum() / (double) queryVoList.size());

            for (QcResultQueryVo itemOp : queryVoList.stream().sorted(Comparator.comparing(QcResultQueryVo::getQcSequence)).collect(Collectors.toList())) {

                if (CollectionUtils.isNotEmpty(iQCMaterialItemEnty.getData()) && iQCMaterialItemEnty.getData().stream().filter(p -> p.getNum().equals(itemOp.getQcSequence()) && p.getIqcMaterialId().equals(itemOp.getMaterialId())).count() > 0) {
                    continue;
                }
                QcResultMaterialItemVo dataItem = new QcResultMaterialItemVo();
                dataItem.setNum(itemOp.getQcSequence());
                dataItem.setIqcMaterialId(itemOp.getMaterialId());
                List<QcResultQueryVo> allThisTime = queryVoList.stream().filter(p -> p.getQcSequence().equals(itemOp.getQcSequence()) && p.getMaterialId().equals(itemOp.getMaterialId())).collect(Collectors.toList());
                for (QcResultQueryVo result : allThisTime) {
                    if (CollectionUtils.isNotEmpty(dataItem.getResultData()) && dataItem.getResultData().stream().filter(p -> p.getTime().equals(DateUtil.format(result.getQcDate(), "MM.dd"))).count() > 0) {
                        continue;
                    }
                    ResultItemVo resultItem = new ResultItemVo();
                    resultItem.setTime(DateUtil.format(result.getQcDate(), "MM.dd"));
                    resultItem.setId(result.getId());
                    resultItem.setQCSequence(result.getQcSequence());
                    resultItem.setResult(result.getResult());
                    resultItem.setNumResult(result.getNumResult());
                    resultItem.setMean(result.getMean());
                    resultItem.setSd(result.getSd());
                    resultItem.setZ(result.getZ());
                    resultItem.setIsAlarm(result.getIsAlarm());
                    resultItem.setAlarmRules(result.getAlarmRules());
                    resultItem.setIsOutControl(result.getIsOutControl());
                    resultItem.setOutControlRules(result.getOutControlRules());
                    resultItem.setIsOutControlHandled(result.getIsOutControlHandled());
                    resultItem.setIsBestInDay(result.getIsBestInDay());
                    if (CollectionUtils.isEmpty(dataItem.getResultData())) {
                        dataItem.setResultData(new ArrayList<>());
                    }
                    dataItem.getResultData().add(resultItem);
                }
                if (CollectionUtils.isEmpty(iQCMaterialItemEnty.getData())) {
                    iQCMaterialItemEnty.setData(new ArrayList<>());
                }
                iQCMaterialItemEnty.getData().add(dataItem);
            }
            if (CollectionUtils.isEmpty(resResult.getIqcMaterials())) {
                resResult.setIqcMaterials(new ArrayList<>());
            }
            resResult.getIqcMaterials().add(iQCMaterialItemEnty);
        }


        return Result.succ(1, "查询成功！", resResult);
    }

    public Result getResultChartTable22(QcResultChartParam qcResultChartParam) {
        Map<String, List> materialResults = getMaterialResults(qcResultChartParam);
        return Result.succ(1, "查询成功！", materialResults);
    }

    /**
     * 获取e-chart 数据集
     *
     * @param qcResultChartParam
     * @return
     */
    public Map<String, Object> getQcResultByQcItemId(QcResultChartParam qcResultChartParam) {
        Map<String, Object> resultMap = new HashMap<>();
        //查字典
        List<Map<String, Object>> qchorizontals = (List<Map<String, Object>>) sysBasDictDetailService.getSysBasDictList("QCHORIZONTAL").getData();
        // 根据质控物ids获取质控物信息
        List<QcMaterial> qcMaterials = getMaterialResultList(qcResultChartParam);
        if (qcMaterials.size() <= 0) {
            return resultMap;
        }
        // 根据结果统计日期X轴
        List<String> xAxis = getResultDateList(qcResultChartParam);
        resultMap.put("xAxis", xAxis);
        // 获取y轴数据
        int[] yAxis = new int[]{-4, -3, -2, -1, 0, 1, 2, 3, 4};
        resultMap.put("yAxis", yAxis);

        // 根据结果获取数据详细信息
        Map<String, List<QcMaterialItemResult>> qcResultMap = getQcMaterialItemMap(qcResultChartParam);

        List<String> yDisplayAxisList = new ArrayList<>();
        if ("LJ".equals(qcResultChartParam.getVal())) {
            // 计算yDisplayAxis
            qcResultMap.forEach((key, value) -> {
                for (int yAxi : yAxis) {
                    double v = value.get(0).getMean() + (value.get(0).getSd() * yAxi);
                    yDisplayAxisList.add(String.valueOf(v));
                }
            });
        }
        resultMap.put("yDisplayAxis", yDisplayAxisList);

        List<QcChartDto> lineData = qcMaterials.stream().map(qcMaterial -> {
            QcChartDto qcChartDto = new QcChartDto();
            qcChartDto.setMaterialsId(qcMaterial.getId());
            qcChartDto.setMaterialName(qcMaterial.getMaterialName());
            if(StringUtils.isNotBlank(qcMaterial.getHorizontal())){
                qcChartDto.setDictName(qchorizontals.stream().filter(a -> a.get("code").equals(qcMaterial.getHorizontal())).findFirst().get().get("name").toString());
                qcChartDto.setDictCode(qchorizontals.stream().filter(a -> a.get("code").equals(qcMaterial.getHorizontal())).findFirst().get().get("code").toString());
                qcChartDto.setDictLineColor(qchorizontals.stream().filter(a -> a.get("code").equals(qcMaterial.getHorizontal())).findFirst().get().get("lineColor").toString());
                qcChartDto.setDictMark(qchorizontals.stream().filter(a -> a.get("code").equals(qcMaterial.getHorizontal())).findFirst().get().get("mark").toString());
            }
            if (qcResultMap.containsKey(qcMaterial.getId())) {
                qcChartDto.setQcResults(qcResultMap.get(qcMaterial.getId()));
            }
            return qcChartDto;
        }).collect(Collectors.toList());
        resultMap.put("lineData", lineData);
        return resultMap;
    }

    /**
     * 查询Z、LJ图 质控结果
     *
     * @return
     */
    public Map<String, List> getMaterialResults(QcResultChartParam qcResultChartParam) {
        Map<String, List> resultMap = new HashMap<>();
        resultMap.put("xDateTimeList", getResultDateList(qcResultChartParam));
        // 获取质控品数据
        List<QcMaterial> materialResultList = getMaterialResultList(qcResultChartParam);
        Map<String, List<QcMaterialItemResult>> qcMaterialItemMap = getQcMaterialItemMap(qcResultChartParam);
        List<QcResultMaterialVo> collect = materialResultList.stream().map(qcMaterial -> {
            QcResultMaterialVo qcResultMaterialVo = new QcResultMaterialVo();
            qcResultMaterialVo.setName(qcMaterial.getMaterialName());
            if (qcMaterialItemMap.containsKey(qcMaterial.getId())) {
                List<QcMaterialItemResult> qcResults = qcMaterialItemMap.get(qcMaterial.getId());
                qcResultMaterialVo.setTotal(qcResults.size());
                // 失控个数
                int count = (int) qcResults.stream().filter(QcMaterialItemResult::getIsOutControl).count();
                qcResultMaterialVo.setOutControlNum(count);
                qcResultMaterialVo.setControlNum(qcResults.size() - count);
                qcResultMaterialVo.setOutControlRate((double) (count / qcResults.size()));
                qcResultMaterialVo.setControlRate(1 - (double) (count / qcResults.size()));
                List<QcResultMaterialItemVo> QcResultMaterialItemVos = new ArrayList<>();
                // 根据项目分组统计
                Map<String, List<QcMaterialItemResult>> QcMaterialItemResultMap = qcResults.stream().collect(groupingBy(QcMaterialItemResult::getMaterialItemId));
                int i = 0;
                for (Map.Entry<String, List<QcMaterialItemResult>> stringListEntry : QcMaterialItemResultMap.entrySet()) {
                    i++;
                    QcResultMaterialItemVo qcResultMaterialItemVo = new QcResultMaterialItemVo();
                    qcResultMaterialItemVo.setIqcMaterialId(stringListEntry.getKey());
                    qcResultMaterialItemVo.setNum(i);
                    qcResultMaterialItemVo.setResultData(null);
                    QcResultMaterialItemVos.add(qcResultMaterialItemVo);
                }
                qcResultMaterialVo.setData(QcResultMaterialItemVos);
            }
            return qcResultMaterialVo;
        }).collect(Collectors.toList());
        resultMap.put("iqcMaterials", collect);
        return resultMap;
    }

    /**
     * 查询 Grubbs 数据 =======================================Grubbs 图=============================
     *
     * @param qcResultChartParam
     */
    @Override
    public Result getGrubbsData(QcResultChartParam qcResultChartParam) {
        List<QcMaterialItemResult> materialItemResultList = getMaterialItemResultList(qcResultChartParam);
        List<QcGrubbsVo> qcGrubbsVos = new ArrayList<>();

        double results[] = new double[materialItemResultList.size()];
        for (int i = 0; i < materialItemResultList.size(); i++) {
            QcGrubbsVo qcGrubbsVo = new QcGrubbsVo();
            qcGrubbsVo.setN(i + 1);
            qcGrubbsVo.setQcDate(materialItemResultList.get(i).getQcDate().toString());
            qcGrubbsVo.setQcSequence(materialItemResultList.get(i).getQcSequence());
            qcGrubbsVo.setResult(materialItemResultList.get(i).getResult());
            qcGrubbsVo.setIsAlarm(false);
            qcGrubbsVo.setIsOutControl(false);
            results[i] = materialItemResultList.get(i).getNumResult();
            if (qcGrubbsVo.getN() <= 2) {
                qcGrubbsVos.add(qcGrubbsVo);
                continue;
            }
            double n2s = GetCriticalValue(qcGrubbsVo.getN(), 0.05 * 2);
            double n3s = GetCriticalValue(qcGrubbsVo.getN(), 0.01 * 2);
            qcGrubbsVo.setN2s(n2s);
            qcGrubbsVo.setN3s(n3s);

            double numResultAvg = Arrays.stream(results).average().orElse(Double.NaN);
            qcGrubbsVo.setMean(numResultAvg);
            qcGrubbsVo.setSd(results.length == 1 ? results[0] : standardDeviation(results));

            double min = Math.abs((Arrays.stream(results).min().getAsDouble() - qcGrubbsVo.getMean()) / qcGrubbsVo.getSd());
            double max = Math.abs((Arrays.stream(results).max().getAsDouble() - qcGrubbsVo.getMean()) / qcGrubbsVo.getSd());
            qcGrubbsVo.setSiMin(min);
            qcGrubbsVo.setSiMax(max);

            if (qcGrubbsVo.getSiMin() > qcGrubbsVo.getSiMax()) {
                double temp = qcGrubbsVo.getSiMin();
                qcGrubbsVo.setSiMin(qcGrubbsVo.getSiMax());
                qcGrubbsVo.setSiMax(temp);
            }
            if (qcGrubbsVo.getSiMax() > qcGrubbsVo.getN3s() || qcGrubbsVo.getSiMin() > qcGrubbsVo.getN3s()) {
                qcGrubbsVo.setIsOutControl(true);
            }
            if (!qcGrubbsVo.getIsOutControl() && (qcGrubbsVo.getSiMax() > qcGrubbsVo.getN2s() || qcGrubbsVo.getSiMin() > qcGrubbsVo.getN2s())) {
                qcGrubbsVo.setIsAlarm(true);
            }
            qcGrubbsVos.add(qcGrubbsVo);
        }
        return Result.succ(1, "查询成功！", qcGrubbsVos);
    }

    /**
     * 查询 Monica 数据 =======================================Monica 图=============================
     *
     * @param qcResultChartParam
     */
    @Override
    public Result getMonicaData(QcResultChartParam qcResultChartParam) {
        List<QcMaterialItemResult> materialItemResultList = getMaterialItemResultList(qcResultChartParam);
        QcMonicaVo qcMonicaVo = new QcMonicaVo();
        if (materialItemResultList.size() > 0) {
            Map<Timestamp, List<QcMaterialItemResult>> dateMap = materialItemResultList.stream().collect(groupingBy(QcMaterialItemResult::getQcDate));
            double[] numResults = materialItemResultList.stream().mapToDouble(qcResult -> qcResult.getNumResult()).toArray();
            double numResultAvg = Arrays.stream(numResults).average().orElse(Double.NaN);
            qcMonicaVo.setT(numResultAvg);
            if (ObjectUtil.isNotEmpty(qcResultChartParam.getCcv())) {
                qcMonicaVo.setCcv(qcResultChartParam.getCcv());
            } else {
                qcMonicaVo.setCcv(numResults.length == 1 ? numResults[0] : standardDeviation(numResults));
            }
            List<QcMonicaResultsVo> qcMonicaResultsVos = new ArrayList<>();

            final AtomicInteger index = new AtomicInteger(1);
            dateMap.forEach((key, value) -> {
                if (value.size() % 2 != 0) {
                    QcMaterialItemResult qcMaterialItemResult = value.stream().reduce((first, second) -> second).orElse(null);
                    value.add(qcMaterialItemResult);
                }
                for (int i = 1; i < value.size() / 2; i++) {
                    List<QcMaterialItemResult> collect = value.stream().skip((i - 1) * 2).sorted(Comparator.comparing(QcMaterialItemResult::getNumResult)).limit(2).collect(Collectors.toList());
                    QcMonicaResultsVo qcMonicaResultsVo = new QcMonicaResultsVo();
                    qcMonicaResultsVo.setIsAlarm(false);
                    qcMonicaResultsVo.setIsOutControl(false);
                    qcMonicaResultsVo.setQcDate(key.toString());
                    qcMonicaResultsVo.setN(index.getAndAdd(1));
                    qcMonicaResultsVo.setMean(Arrays.stream(collect.stream().mapToDouble(QcMaterialItemResult::getNumResult).toArray()).average().getAsDouble());
                    qcMonicaResultsVo.setMin(collect.stream().findFirst().get());
                    qcMonicaResultsVo.setMax(collect.stream().reduce((first, second) -> second).orElse(null));
                    if (qcMonicaResultsVo.getMean() > qcMonicaVo.getT() + qcMonicaVo.getT() * qcMonicaVo.getCcv() * 1.5 || qcMonicaResultsVo.getMean() < qcMonicaVo.getT() - qcMonicaVo.getT() * qcMonicaVo.getCcv() * 1.5) {
                        qcMonicaResultsVo.setIsOutControl(true);
                    } else if (qcMonicaResultsVo.getMean() > qcMonicaVo.getT() + qcMonicaVo.getT() * qcMonicaVo.getCcv() * 0.8 || qcMonicaResultsVo.getMean() < qcMonicaVo.getT() - qcMonicaVo.getT() * qcMonicaVo.getCcv() * 0.8) {
                        qcMonicaResultsVo.setIsAlarm(true);
                    }
                    qcMonicaResultsVos.add(qcMonicaResultsVo);
                }
            });
            qcMonicaVo.setResults(qcMonicaResultsVos);
        }
        return Result.succ(1, "查询成功！", qcMonicaVo);
    }

    /**
     * 查询 Youden 图数据 =======================================Youden 图=============================
     *
     * @param qcResultChartParam
     * @return
     */
    @Override
    public Result getYouDenData(QcResultChartParam qcResultChartParam) {
        String remove = qcResultChartParam.getQcMaterialIds().remove(1);
        List<QcMaterialItemResult> mainListSource = getMaterialItemResultList(qcResultChartParam);
        qcResultChartParam.getQcMaterialIds().remove(0);
        qcResultChartParam.getQcMaterialIds().add(remove);
        List<QcMaterialItemResult> subListSource = getMaterialItemResultList(qcResultChartParam);
        List<QcMaterialItemResult> mainList = mainListSource.stream().filter(t1 -> subListSource.stream().anyMatch(t2 -> t1.getQcDate().toString().equals(t2.getQcDate().toString()) && t1.getQcSequence() == t2.getQcSequence())).collect(Collectors.toList());
        List<QcMaterialItemResult> subList = subListSource.stream().filter(t1 -> mainListSource.stream().anyMatch(t2 -> t1.getQcDate().toString().equals(t2.getQcDate().toString()) && t1.getQcSequence() == t2.getQcSequence())).collect(Collectors.toList());
        if (mainList.size() < 3) {
            return Result.fail("数据不足，无法做图！", null);
        }
        Map<String, Object> resultMap = new HashMap<>();
        // 计算参数值
        double[] xs = mainList.stream().mapToDouble(qcResult -> qcResult.getNumResult()).toArray();
        double[] ys = subList.stream().mapToDouble(qcResult -> qcResult.getNumResult()).toArray();
        int m = subList.size();
        double sumX = Arrays.stream(xs).sum();
        double sumY = Arrays.stream(ys).sum();
        double sumSqX = Arrays.stream(xs).map(p -> p * p).sum();
        double sumSqY = Arrays.stream(ys).map(p -> p * p).sum();
        double sumProduct = SumProduct(xs, ys);
        double q = sumSqX - sumX * sumX / m;
        double s = sumSqY - sumY * sumY / m;
        double d = sumProduct - sumX * sumY / m;
        double ʎ1 = ((s + q) + Math.sqrt((s - q) * (s - q) + 4 * d * d)) / 2;
        double ʎ2 = ((s + q) - Math.sqrt((s - q) * (s - q) + 4 * d * d)) / 2;
        double tany = ((s - q) - Math.sqrt((s - q) * (s - q) + 4 * d * d)) / 2 / d;
        double ʏ = Math.atan(tany);
        double xMedian;
        if (xs.length % 2 == 0) {
            xMedian = xs[xs.length / 2 - 1];
        } else {
            xMedian = xs[(xs.length - 1) / 2];
        }
        double yMedian;
        if (ys.length % 2 == 0) {
            yMedian = ys[ys.length / 2 - 1];
        } else {
            yMedian = ys[(ys.length - 1) / 2];
        }
        double f = getFinv(m - 2, 2, 0.05);
        double hlp = Math.sqrt(2 * ʎ1 * f / (m - 2));
        double hlq = Math.sqrt(2 * ʎ2 * f / (m - 2));


        // region 椭圆
        double preHlp = hlp / 90;
        LinkedList<QcYoudenOvalDataVo> qcYouDenOvalDataVos = new LinkedList<>();
        for (int i = 0; i < 180; i++) {
            QcYoudenOvalDataVo qcYouDenOvalDataFirstVo = new QcYoudenOvalDataVo();
            double x = hlp - preHlp * i;
            double y = GetY(hlp, hlq, x);
            qcYouDenOvalDataFirstVo.setN(i);
            qcYouDenOvalDataFirstVo.setOvalX(x);
            qcYouDenOvalDataFirstVo.setOvalY(y);
            double X = xMedian + x * Math.cos(ʏ) - y * Math.sin(ʏ);
            double Y = yMedian - x * Math.sin(ʏ) + y * Math.cos(ʏ);
            qcYouDenOvalDataFirstVo.setX(X);
            qcYouDenOvalDataFirstVo.setY(Y);
            qcYouDenOvalDataVos.addFirst(qcYouDenOvalDataFirstVo);
            if (i > 0) {
                QcYoudenOvalDataVo qcYouDenOvalDataLastVo = new QcYoudenOvalDataVo();
                qcYouDenOvalDataLastVo.setN(i * -1);
                qcYouDenOvalDataLastVo.setOvalX(x);
                qcYouDenOvalDataLastVo.setOvalY(y * -1);
                qcYouDenOvalDataLastVo.setX(xMedian + x * Math.cos(ʏ) + y * Math.sin(ʏ));
                qcYouDenOvalDataLastVo.setY(yMedian - x * Math.sin(ʏ) - y * Math.cos(ʏ));
                qcYouDenOvalDataVos.addLast(qcYouDenOvalDataLastVo);
            }
        }
        // 补点 形成完整的圆
        qcYouDenOvalDataVos.addFirst(new QcYoudenOvalDataVo(180, hlp * -1, 0.0, xMedian - hlp * Math.cos(ʏ), yMedian + hlp * Math.sin(ʏ)));
        qcYouDenOvalDataVos.addLast(new QcYoudenOvalDataVo(-180, hlp * -1, 0.0, xMedian - hlp * Math.cos(ʏ), yMedian + hlp * Math.sin(ʏ)));
        final double xMedian1 = xMedian;
        final double yMedian1 = yMedian;
        List<QcYoudenOvalDataVo> ovalData = qcYouDenOvalDataVos.stream().map(qcYoudenOvalDataVo -> {
            qcYoudenOvalDataVo.setChartX(qcYoudenOvalDataVo.getX() - xMedian1);
            qcYoudenOvalDataVo.setChartY(qcYoudenOvalDataVo.getY() - yMedian1);
            return qcYoudenOvalDataVo;
        }).collect(Collectors.toCollection(LinkedList::new));
        resultMap.put("ovalData", ovalData);

        //散点 #endregion'
        resultMap.put("MainMedian", xMedian);
        resultMap.put("SubMedian", yMedian);
        resultMap.put("MainSD", xs.length == 1 ? xs[0] : standardDeviation(xs));
        resultMap.put("SubSD", ys.length == 1 ? ys[0] : standardDeviation(ys));

        int Quadrant1Count = 0;
        int Quadrant2Count = 0;
        int Quadrant3Count = 0;
        int Quadrant4Count = 0;
        List<QcYoudenResultVo> results = new ArrayList<>();
        for (int i = 0; i < xs.length; i++) {
            mainList.get(i).setChartResult(mainList.get(i).getNumResult() - xMedian);
            subList.get(i).setChartResult(subList.get(i).getNumResult() - yMedian);
            QcYoudenResultVo qcYoudenResultVo = new QcYoudenResultVo();
            qcYoudenResultVo.setN(i + 1);
            qcYoudenResultVo.setMain(mainList.get(i));
            qcYoudenResultVo.setSub(subList.get(i));
            qcYoudenResultVo.setQcDate(mainList.get(i).getQcDate().toString());
            qcYoudenResultVo.setQcSequence(mainList.get(i).getQcSequence());
            //判断是否离群
            qcYoudenResultVo.setOutlier(IsOutlier(ovalData, qcYoudenResultVo));
            //判断象限
            if (qcYoudenResultVo.getMain().getNumResult() >= xMedian) {
                if (qcYoudenResultVo.getSub().getNumResult() >= yMedian) {
                    qcYoudenResultVo.setQuadrant(1);
                    Quadrant1Count += 1;
                } else {
                    qcYoudenResultVo.setQuadrant(4);
                    Quadrant4Count += 1;
                }
            } else {
                if (qcYoudenResultVo.getSub().getNumResult() >= yMedian) {
                    qcYoudenResultVo.setQuadrant(2);
                    Quadrant2Count += 1;
                } else {
                    qcYoudenResultVo.setQuadrant(3);
                    Quadrant3Count += 1;
                }
            }
            results.add(qcYoudenResultVo);
        }
        resultMap.put("Quadrant1Count", Quadrant1Count);
        resultMap.put("Quadrant2Count", Quadrant2Count);
        resultMap.put("Quadrant3Count", Quadrant3Count);
        resultMap.put("Quadrant4Count", Quadrant4Count);
        resultMap.put("results", results);
        return Result.succ(1, "查询成功！", resultMap);
    }

    /**
     * 查询 正态分布 数据 =======================================正态分布 图=============================
     *
     * @param qcResultChartParam
     * @return
     */
    @Override
    public Result getNormalDistributionData(QcResultChartParam qcResultChartParam) {
        Map<String, List> resultMap = new HashMap<>();
        List<QcMaterialItemResult> materialItemResultList = getMaterialItemResultList(qcResultChartParam);
        if (materialItemResultList.size() > 0) {
            double[] numResults = materialItemResultList.stream().mapToDouble(qcResult -> qcResult.getNumResult()).toArray();
            double max = Arrays.stream(numResults).max().getAsDouble();
            double min = Arrays.stream(numResults).min().getAsDouble();
            double range = max - min;
            double sqrt = Math.sqrt(numResults.length);
            double group = BigDecimal.valueOf(sqrt).setScale(0, RoundingMode.CEILING).doubleValue();
            double interval = range / group;
            double avg = Arrays.stream(numResults).average().orElse(Double.NaN);
            double sd = numResults.length == 1 ? numResults[0] : standardDeviation(numResults);

            // groups
            List<QcNormalDistributionGroupVo> qcNormalDistributionGroupVos = new ArrayList<>();
            for (int i = 1; i <= group; i++) {
                QcNormalDistributionGroupVo qcNormalDistributionGroupVo = new QcNormalDistributionGroupVo();
                qcNormalDistributionGroupVo.setN(i);
                qcNormalDistributionGroupVo.setMin(min + (i - 1) * interval);
                qcNormalDistributionGroupVo.setMax(min + i * interval);
                if (i == group) {
                    List<QcMaterialItemResult> details = materialItemResultList.stream().filter(result -> result.getNumResult() >= qcNormalDistributionGroupVo.getMin() && result.getNumResult() <= qcNormalDistributionGroupVo.getMax()).collect(Collectors.toList());
                    qcNormalDistributionGroupVo.setDetail(details);
                } else {
                    List<QcMaterialItemResult> details = materialItemResultList.stream().filter(result -> result.getNumResult() >= qcNormalDistributionGroupVo.getMin() && result.getNumResult() < qcNormalDistributionGroupVo.getMax()).collect(Collectors.toList());
                    qcNormalDistributionGroupVo.setDetail(details);
                }
                qcNormalDistributionGroupVo.setCount(qcNormalDistributionGroupVo.getDetail().size());
                NormalDistribution normalDist = new NormalDistribution(avg, sd);
                // 计算累积分布函数（CDF）
//                double result = normalDist.cumulativeProbability(max);
                // 计算概率密度函数（PDF）
                qcNormalDistributionGroupVo.setNormDist(normalDist.density(max));
                qcNormalDistributionGroupVos.add(qcNormalDistributionGroupVo);
            }

            // 正态图
            int ndRate = 20;
            double ndCount = group * ndRate;
            double ndPre = (max - min) / ndCount;
            List<QcNormalDistributionNormDistVo> qcNormalDistributionNormDistVos = new ArrayList<>();
            for (int i = 0; i < ndCount; i++) {
                QcNormalDistributionNormDistVo qcNormalDistributionNormDistVo = new QcNormalDistributionNormDistVo();
                qcNormalDistributionNormDistVo.setN(i);
                qcNormalDistributionNormDistVo.setMin(min + (i + ndRate / 2) * ndPre);
                qcNormalDistributionNormDistVo.setMax(min + (i + 1 + ndRate / 2) * ndPre);
                NormalDistribution normalDist = new NormalDistribution(avg, sd);
                double density = normalDist.density((qcNormalDistributionNormDistVo.getMin() + qcNormalDistributionNormDistVo.getMax()) / 2);
                qcNormalDistributionNormDistVo.setValue(density);
                qcNormalDistributionNormDistVos.add(qcNormalDistributionNormDistVo);
            }
            resultMap.put("groups", qcNormalDistributionGroupVos);
            resultMap.put("normDists", qcNormalDistributionNormDistVos);
        }
        return Result.succ(1, "查询成功！", resultMap);
    }

    /**
     * 查询 累计和控制图 数据 =======================================累计和控制 图=============================
     *
     * @param qcResultChartParam
     * @return
     */
    @Override
    public Result getCusumData(QcResultChartParam qcResultChartParam) {
        QcCusumVo qcCusumVo = new QcCusumVo();
        List<QcMaterialItemResult> materialItemResultList = getMaterialItemResultList(qcResultChartParam);
        if (materialItemResultList.size() > 0) {
            qcCusumVo.setResults(materialItemResultList);
            for (int i = 1; i < qcCusumVo.getResults().size(); i++) {
                double mr = Math.abs(qcCusumVo.getResults().get(i).getNumResult() - qcCusumVo.getResults().get(i - 1).getNumResult());
                qcCusumVo.getResults().get(i).setMovingRange(mr);
            }
            qcCusumVo.getResults().get(0).setMovingRange(Double.NaN);
            double d2 = 1.128;
            double[] movingRanges = qcCusumVo.getResults().stream().filter(item -> !Double.isNaN(item.getMovingRange())).mapToDouble(qcResult -> qcResult.getMovingRange()).toArray();
            double mrAvg = Arrays.stream(movingRanges).average().orElse(Double.NaN);
            double sigma = mrAvg / d2;
            double k = 0.5 * sigma;
            double ucl = 4 * sigma;
            double lcl = -4 * sigma;
            double[] numResults = qcCusumVo.getResults().stream().mapToDouble(qcResult -> qcResult.getNumResult()).toArray();
            double mean = Arrays.stream(numResults).average().orElse(Double.NaN);
            double targetValue = ObjectUtil.isNull(qcResultChartParam.getTargetValue()) || Double.isNaN(qcResultChartParam.getTargetValue()) ? mean : qcResultChartParam.getTargetValue();
            qcCusumVo.setMean(mean);
            qcCusumVo.setTargetValue(targetValue);
            qcCusumVo.setUcl(ucl);
            qcCusumVo.setLcl(lcl);
            for (int i = 0; i < qcCusumVo.getResults().size(); i++) {
                QcMaterialItemResult qcMaterialItemResult = qcCusumVo.getResults().get(i);
                qcCusumVo.getResults().get(i).setN(i + 1);
                qcCusumVo.getResults().get(i).setIsAlarm(false);
                qcCusumVo.getResults().get(i).setIsOutControl(false);
                double uc = Math.max(0, i == 0 ? 0 : qcCusumVo.getResults().get(i - 1).getUpperCUSUM() + qcMaterialItemResult.getNumResult() - targetValue - k);
                double lc = Math.max(0, i == 0 ? 0 : qcCusumVo.getResults().get(i - 1).getLowCUSUM() + qcMaterialItemResult.getNumResult() - targetValue + k);
                qcCusumVo.getResults().get(i).setUpperCUSUM(uc);
                qcCusumVo.getResults().get(i).setLowCUSUM(lc);
                if (uc > ucl || lc < lcl) {
                    qcCusumVo.getResults().get(i).setIsOutControl(true);
                }
            }
        }
        return Result.succ(1, "查询成功！", qcCusumVo);
    }

    /**
     * 查询质控品数据信息
     */
    public List<QcMaterial> getMaterialResultList(QcResultChartParam qcResultChartParam) {
        LambdaQueryWrapper<QcMaterial> materialQWrapper = new LambdaQueryWrapper<>();
        materialQWrapper
                .eq(QcMaterial::getDelFlag, 0)
                .in(QcMaterial::getId, qcResultChartParam.QcMaterialIds);
        List<QcMaterial> qcMaterials = qcMaterialMapper.selectList(materialQWrapper);
        return qcMaterials;
    }

    /**
     * 查询质控结果数据信息
     */
    public List<QcMaterialItemResult> getMaterialItemResultList(QcResultChartParam qcResultChartParam) {
        LambdaQueryWrapper<QcMaterialItemResult> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(QcMaterialItemResult::getDelFlag, 0)
                .eq(QcMaterialItemResult::getItemDictId, qcResultChartParam.itemDictId)
                .in(QcMaterialItemResult::getMaterialId, qcResultChartParam.getQcMaterialIds())
                .ge(QcMaterialItemResult::getQcDate, new Timestamp(qcResultChartParam.QCDateS.getTime()))
                .le(QcMaterialItemResult::getQcDate, new Timestamp(qcResultChartParam.QCDateE.getTime()))
                .orderByAsc(QcMaterialItemResult::getQcDate, QcMaterialItemResult::getQcSequence);
        List<QcMaterialItemResult> materialItemResultList = qcMaterialItemResultMapper.selectList(queryWrapper);
        return materialItemResultList;
    }

    /**
     * 过滤去重日期
     *
     * @param qcResultChartParam
     * @return 字符日期数组
     */
    public List<String> getResultDateList(QcResultChartParam qcResultChartParam) {
        List<QcMaterialItemResult> materialItemResultList = getMaterialItemResultList(qcResultChartParam);
        if (materialItemResultList.size() <= 0) {
            return new ArrayList<>();
        }
        return materialItemResultList.stream().map(result -> DateUtil.format(result.getQcDate(), "MM.dd")).distinct().collect(Collectors.toList());
    }

    /**
     * 获取质控结果分组信息
     *
     * @param qcResultChartParam
     * @return
     */
    public Map<String, List<QcMaterialItemResult>> getQcMaterialItemMap(QcResultChartParam qcResultChartParam) {
        List<QcMaterialItemResult> materialItemResultList = getMaterialItemResultList(qcResultChartParam);
        if (materialItemResultList.size() <= 0) {
            return new HashMap<>();
        }
        return materialItemResultList.stream().map(qcResult -> {
            qcResult.setQcDateStr(DateUtil.format(qcResult.getQcDate(), "MM.dd"));
            return qcResult;
        }).collect(groupingBy(QcMaterialItemResult::getMaterialId));
    }

    // 计算T分布的逆运算
    public double GetCriticalValue(int n, double alpha) {
        if (n < 3) {
            return 0;
        }
        if (alpha < 0) {
            return 0;
        }
        TDistribution tDistribution = new TDistribution(n - 2);

        double tInv = tDistribution.inverseCumulativeProbability(alpha / n);
        double t = Math.pow(tInv, 2);
        double result = (n - 1) / Math.sqrt(n) * Math.sqrt(t / (n - 2 + t));
        return result;
    }

    // 计算F概率分布的反函数值
    private static double getFinv(int numeratorDegreesOfFreedom, int denominatorDegreesOfFreedom, double p) {
        FDistributionImpl fDistribution = new FDistributionImpl(numeratorDegreesOfFreedom, denominatorDegreesOfFreedom);
        double v;
        try {
            v = fDistribution.inverseCumulativeProbability(p);
        } catch (MathException e) {
            throw new RuntimeException(e);
        }
        return Math.pow(v, -1);
    }


    private double standardDeviation(double[] data) {
        // 计算平均值
        double mean = Arrays.stream(data).average().orElse(Double.NaN);
        // 计算每个数值与平均值的差的平方
        double sumOfSquares = Arrays.stream(data).map(x -> Math.pow(x - mean, 2)).sum();
        // 计算方差
        double variance = sumOfSquares / data.length;
        // 计算标准差
        double standardDeviation = Math.sqrt(variance);
        return standardDeviation;
    }

    private static Boolean IsOutlier(List<QcYoudenOvalDataVo> ovalDatas, QcYoudenResultVo data) {
        double x = data.getMain().getChartResult();
        double y = data.getSub().getChartResult();

        //结果值超出椭圆上下限
        if (Math.abs(x) > ovalDatas.stream().mapToDouble(item -> item.getChartX()).max().getAsDouble() || Math.abs(y) > ovalDatas.stream().mapToDouble(item -> item.getChartY()).max().getAsDouble()) {
            return true;
        }
        double p1 = Double.NaN, p2 = Double.NaN;

        //正序查找
        for (int i = 1; i < ovalDatas.size(); i++) {
            if ((ovalDatas.get(i - 1).getChartX() <= x && ovalDatas.get(i).getChartX() >= x) || (ovalDatas.get(i - 1).getChartX() >= x && ovalDatas.get(i).getChartX() <= x)) {
                p1 = (ovalDatas.get(i - 1).getChartY() + ovalDatas.get(i).getChartY()) / 2;
                break;
            }
        }

        //倒序查找
        for (int i = ovalDatas.size() - 1; i > 1; i--) {
            if ((ovalDatas.get(i).getChartX() >= x && ovalDatas.get(i - 1).getChartX() <= x) || (ovalDatas.get(i).getChartX() <= x && ovalDatas.get(i - 1).getChartX() >= x)) {
                p2 = (ovalDatas.get(i - 1).getChartY() + ovalDatas.get(i).getChartY()) / 2;
                break;
            }
        }

        if (Double.isNaN(p1) || Double.isNaN(p2)) {
            return true;
        }
        if (y > Math.max(p1, p2) || y < Math.min(p1, p2)) {
            return true;
        }
        return false;
    }

    private static double SumProduct(double[] xs, double[] ys) {
        double result = 0;
        if (xs.length != ys.length) {
            return result;
        }
        for (int i = 0; i < xs.length; i++) {
            result += xs[i] * ys[i];
        }
        return result;
    }

    private static double GetY(double hlp, double hlq, double x) {
        return hlq * Math.sqrt(1 - x * x / (hlp * hlp));
    }


}
